﻿//-----------------------------------------------------------------------
// <copyright>
//     Copyright (c) Artur Mustafin. All rights reserved.
// </copyright>
//-----------------------------------------------------------------------

namespace Expressions.Compiler.Internal
{
    using System;
    using System.Collections.Generic;

    /// <summary>
    /// Class for gramma rule definitions
    /// </summary>
    internal sealed class Rule<T>
    {
        /// <summary>
        /// Lookup table
        /// </summary>
        private readonly HashSet<Terminal<T>> _first;

        /// <summary>
        /// Rule definition
        /// </summary>
        private readonly Rules<T>.Definition _rule;

        /// <summary>
        /// 
        /// </summary>
        private readonly Nonterminal<T> _nonterminal;

        /// <summary>
        /// A dictionary
        /// </summary>
        private static Dictionary<Rules<T>.Definition, Rule<T>> _rules = new Dictionary<Rules<T>.Definition, Rule<T>>();

        /// <summary>
        /// Initializes a new instance of the <see cref="Rule"/> class.
        /// </summary>
        /// <param name="nonterminal">The input rule</param>
        internal Rule(Nonterminal<T> nonterminal, Rules<T>.Definition rule)
        {
            _nonterminal = nonterminal;
            _rule = rule;
            _first = new HashSet<Terminal<T>>();
        }

        /// <summary>
        /// Set of terminal tokens.
        /// </summary>
        /// <value>The terminals.</value>
        internal IEnumerable<Terminal<T>> First
        {
            get
            {
                return _first;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Rule"/> class.
        /// </summary>
        /// <param name="nonterminal">The input rule</param>
        private Rule(Rules<T>.Definition rule)
        {
            _rule = rule;
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Rules.Definition"/> to <see cref="Expressions.Compiler.Rule"/>.
        /// </summary>
        /// <param name="rule">The input rule</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Rule<T>(Rules<T>.Definition rule)
        {
            if (rule != null)
            {
                return GetRule(null, rule);
            }
            return null;
        }

        private Tokens<T> _tokens;

        /// <summary>
        /// Evaluates this instance.
        /// </summary>
        /// <returns>A token stream</returns>
        internal bool Parse(Parser<T> parser)
        {
            if (_tokens.Parse(parser))
            {
                parser.Accept();
                return true;
            }
            parser.Cancel();
            return false;
        }

#if AMIBIGOUS
        
#if TRACE
        public override string ToString()
        {
            return _tokens.ToString();
        }
#endif

#endif

        /// <summary>
        /// Gets the rule.
        /// </summary>
        /// <param name="definition">The input rule</param>
        /// <returns>An input rule</returns>
        internal static Rule<T> GetRule(Nonterminal<T> nonterminal, Rules<T>.Definition definition)
        {
            if (nonterminal != null)
            {
                return nonterminal[definition];
            }
            if (!_rules.ContainsKey(definition))
            {
                Rule<T> rule = new Rule<T>(definition);
                _rules.Add(definition, rule);
                return rule;
            }
            return _rules[definition];
        }

        /// <summary>
        /// Gets the first terminal token for rule from the specified parent.
        /// </summary>
        /// <param name="parent">The parent.</param>
        internal void InitializeInternal()
        {
            if (_tokens == null)
            {
                _tokens = _rule();
            }
        }

        /// <summary>
        /// Validates this instance.
        /// </summary>
        /// <param name="parent">The parent.</param>
        internal void Validate(Nonterminal<T> parent)
        {
            ValidateInternal();
            if (_tokens != null)
            {
                foreach (Token<T> token in _tokens.Items)
                {
                    Reference<T> reference = token.Entity as Reference<T>;
                    if (reference != null)
                    {
                        Nonterminal<T> nonterminal = reference.Nonterminal;
                        if (nonterminal != null)
                        {
                            if (!token.Optional)
                            {
                                if (!parent.Visited(nonterminal))
                                {
                                    nonterminal.ValidateNonterminal(parent);
                                }
                                else
                                {
                                    if (nonterminal.Visited(parent))
                                    {
                                        throw new NotSupportedException("Initialization of the circle referenced instruction is not supported");
                                    }
                                }
                            }
                        }
                    }
                }
            }
            if (_first.Count == 0)
            {
                throw new NotSupportedException("Initialization of the empty lookup table rule is not supported");
            }
        }

        /// <summary>
        /// Initializes the rule using the specified parent hash set.
        /// </summary>
        /// <param name="parent">The parent.</param>
        internal void Initialize(Nonterminal<T> parent)
        {
            InitializeInternal();
            if (_tokens != null)
            {
                foreach (Token<T> token in _tokens.Items)
                {
                    Terminal<T> terminal = token.Entity as Terminal<T>;
                    if (terminal != null)
                    {
                        _first.Add(terminal);
                    }
                }
                foreach (Token<T> token in _tokens.Items)
                {
                    Reference<T> reference = token.Entity as Reference<T>;
                    if (reference != null)
                    {
                        Nonterminal<T> nonterminal = reference.Nonterminal;
                        if (nonterminal != null)
                        {
                            if (!parent.Visited(nonterminal))
                            {
                                nonterminal.InitializeNonterminal(parent);
                            }
                        }
                    }
                }
                foreach (Token<T> token in _tokens.Items)
                {
                    Reference<T> reference = token.Entity as Reference<T>;
                    if (reference != null)
                    {
                        foreach (Terminal<T> terminal in reference.Nonterminal.First(parent, token.Optional))
                        {
                            _first.Add(terminal);
                        }
                        if (!reference.Optional)
                        {
                            break;
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Validates this instance.
        /// </summary>
        private void ValidateInternal()
        {
            if (_tokens == null)
            {
                throw new NotSupportedException("Initialization of the rule returning null is not supported");
            }
            IEnumerator<Token<T>> enumerator = _tokens.Items.GetEnumerator();
            if (!enumerator.MoveNext())
            {
                throw new NotSupportedException("Initialization of the rule returning empty set is not supported");
            }
            Reference<T> reference = enumerator.Current.Entity as Reference<T>;
            if (reference != null)
            {
                if (reference.Nonterminal == _nonterminal)
                {
                    if (reference.Optional)
                    {
                        throw new NotSupportedException("Initialization of the self-referenced head optional rule is not supported");
                    }
                }
            }
            while (enumerator.MoveNext())
            {
                reference = enumerator.Current.Entity as Reference<T>;
                if (reference != null)
                {
                    if (reference.Nonterminal == _nonterminal)
                    {
                        if (!reference.Optional)
                        {
                            throw new NotSupportedException("Initialization of the self-referenced non-optional rule is not supported");
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets the definition.
        /// </summary>
        /// <value>The definition.</value>
        internal Rules<T>.Definition Definition
        {
            get
            {
                return _rule;
            }
        }
    }

    /// <summary>
    /// Class for gramma rule definitions
    /// </summary>
    internal sealed class Rule
    {
        /// <summary>
        /// Lookup table
        /// </summary>
        private readonly HashSet<Terminal> _first;

        /// <summary>
        /// Rule definition
        /// </summary>
        private readonly Rules.Definition _rule;

        /// <summary>
        /// 
        /// </summary>
        private readonly Nonterminal _nonterminal;

        /// <summary>
        /// A dictionary
        /// </summary>
        private static Dictionary<Rules.Definition, Rule> _rules = new Dictionary<Rules.Definition, Rule>();

        /// <summary>
        /// Initializes a new instance of the <see cref="Rule"/> class.
        /// </summary>
        /// <param name="nonterminal">The input rule</param>
        internal Rule(Nonterminal nonterminal, Rules.Definition rule)
        {
            _nonterminal = nonterminal;
            _rule = rule;
            _first = new HashSet<Terminal>();
        }

        /// <summary>
        /// Set of terminal tokens.
        /// </summary>
        /// <value>The terminals.</value>
        internal IEnumerable<Terminal> First
        {
            get
            {
                return _first;
            }
        }

        /// <summary>
        /// Initializes a new instance of the <see cref="Rule"/> class.
        /// </summary>
        /// <param name="nonterminal">The input rule</param>
        private Rule(Rules.Definition rule)
        {
            _rule = rule;
        }

        /// <summary>
        /// Performs an implicit conversion from <see cref="Expressions.Compiler.Rules.Definition"/> to <see cref="Expressions.Compiler.Rule"/>.
        /// </summary>
        /// <param name="rule">The input rule</param>
        /// <returns>The result of the conversion.</returns>
        public static implicit operator Rule(Rules.Definition rule)
        {
            if (rule != null)
            {
                return GetRule(null, rule);
            }
            return null;
        }

        private Tokens _tokens;

        /// <summary>
        /// Evaluates this instance.
        /// </summary>
        /// <returns>A token stream</returns>
        internal bool Parse(Parser parser)
        {
            if (_tokens.Parse(parser))
            {
                parser.Accept();
                return true;
            }
            parser.Cancel();
            return false;
        }

#if AMIBIGOUS
        
#if TRACE
        public override string ToString()
        {
            return _tokens.ToString();
        }
#endif

#endif

        /// <summary>
        /// Gets the rule.
        /// </summary>
        /// <param name="definition">The input rule</param>
        /// <returns>An input rule</returns>
        internal static Rule GetRule(Nonterminal nonterminal, Rules.Definition definition)
        {
            if (nonterminal != null)
            {
                return nonterminal[definition];
            }
            if (!_rules.ContainsKey(definition))
            {
                Rule rule = new Rule(definition);
                _rules.Add(definition, rule);
                return rule;
            }
            return _rules[definition];
        }

        /// <summary>
        /// Gets the first terminal token for rule from the specified parent.
        /// </summary>
        /// <param name="parent">The parent.</param>
        internal void InitializeInternal()
        {
            if (_tokens == null)
            {
                _tokens = _rule();
            }
        }

        /// <summary>
        /// Validates this instance.
        /// </summary>
        /// <param name="parent">The parent.</param>
        internal void Validate(Nonterminal parent)
        {
            ValidateInternal();
            foreach (Token token in _tokens.Items)
            {
                Nonterminal nonterminal = GetNonterminal(token);
                if (nonterminal != null)
                {
                    if (!parent.Visit(nonterminal))
                    {
                        nonterminal.ValidateNonterminal(parent);
                    }
                }
            }
            if (_first.Count == 0)
            {
                throw new NotSupportedException("Initialization of the empty lookup table rule is not supported");
            }
        }

        /// <summary>
        /// Initializes the rule using the specified parent hash set.
        /// </summary>
        /// <param name="parent">The parent.</param>
        internal void Initialize(Nonterminal parent)
        {
            InitializeInternal();
            if (_tokens != null)
            {
                foreach (Token token in _tokens.Items)
                {
                    Nonterminal nonterminal = GetNonterminal(token);
                    if (nonterminal != null)
                    {
                        if (!parent.Visit(nonterminal))
                        {
                            nonterminal.InitializeNonterminal(parent);
                        }
                    }
                }
                foreach (Token token in _tokens.Items)
                {
                    Reference reference = token.Entity as Reference;
                    if (reference != null)
                    {
                        foreach (Terminal referenceTerminal in reference.Nonterminal.First(parent))
                        {
                            _first.Add(referenceTerminal[token.Optional]);
                        }
                        if (!reference.Optional)
                        {
                            break;
                        }
                    }
                    Terminal terminal = GetTerminal(token);
                    if (terminal != null)
                    {
                        _first.Add(terminal);
                        break;
                    }
                }
            }
        }

        /// <summary>
        /// Validates this instance.
        /// </summary>
        private void ValidateInternal()
        {
            if (_tokens == null)
            {
                throw new NotSupportedException("Initialization of the rule returning null is not supported");
            }
            IEnumerator<Token> enumerator = _tokens.Items.GetEnumerator();
            if (!enumerator.MoveNext())
            {
                throw new NotSupportedException("Initialization of the rule returning empty set is not supported");
            }
            Reference reference = enumerator.Current.Entity as Reference;
            if (reference != null)
            {
                if (reference.Nonterminal == _nonterminal)
                {
                    if (reference.Optional)
                    {
                        throw new NotSupportedException("Initialization of the self-referenced head optional rule is not supported");
                    }
                }
            }
            while (enumerator.MoveNext())
            {
                reference = enumerator.Current.Entity as Reference;
                if (reference != null)
                {
                    if (reference.Nonterminal == _nonterminal)
                    {
                        if (!reference.Optional)
                        {
                            throw new NotSupportedException("Initialization of the self-referenced non-optional rule is not supported");
                        }
                    }
                }
            }
        }

        /// <summary>
        /// Gets the nonterminal.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        private Nonterminal GetNonterminal(Token token)
        {
            Reference reference = token.Entity as Reference;
            if (reference != null)
            {
                return reference.Nonterminal;
            }
            return null;
        }

        /// <summary>
        /// Gets the terminal.
        /// </summary>
        /// <param name="token">The token.</param>
        /// <returns></returns>
        private Terminal GetTerminal(Token token)
        {
            Reference reference = token.Entity as Reference;
            if (reference == null)
            {
                return token.Entity as Terminal;
            }
            return null;
        }

        /// <summary>
        /// Gets the definition.
        /// </summary>
        /// <value>The definition.</value>
        internal Rules.Definition Definition
        {
            get
            {
                return _rule;
            }
        }
    }
}
